package com.geeyuu.android.service;

import java.io.UnsupportedEncodingException;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.http.util.ByteArrayBuffer;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;

import com.geeyuu.android.GeePrefrence;
import com.geeyuu.android.utils.AndroidUtil;
import com.geeyuu.android.utils.CommonUtil;
import com.geeyuu.android.utils.LogUtil;
import com.geeyuu.android.utils.ProtocolUtil;

public class RECmd{
    private static final String TAG = "RECmd";
    
    public String tag;
    
    /**
     * 最大不超过HEART_BEAT_SEQUENCE_ID
     */
    public static final int MAX_SEQUENCE_ID = 10000;
    /**
     * 最大不超过65535
     */
    public static final int HEART_BEAT_SEQUENCE_ID = 10001;
    public static final int RELOGIN_SEQUENCE_ID = 10002;
    public static final int REPORT_LOC_SEQUENCE_ID = 10003;
    
    private static final int MY_SHARE_LIST_SEQUENCE_ID = 10004;
    private static final int MY_BLACK_LIST_SEQUENCE_ID = 10005;
    private static final int MY_ATTENTION_LIST_SEQUENCE_ID = 10006;
    
    public boolean isServerException = false;
    
    public boolean isNeedNotifyUI = true;
    
    /**
     * 包头长度
     */
    private static final int PACKAGE_HEAD_LENGTH = 16;
    
    // common
    int protocolVersion; // uint16
    long sessionId; // uint32
    
    /**
     * 这个值到Service里才生成
     */
    int sequenceID; // uint16
    
    int cmd; // uint16
    // Date lastSendTime; // 客户端时间
    
    int packageLength; // uint16
    
    int cmdType = CMD_TYPE_DEFAULT;
    public static final int CMD_TYPE_DEFAULT = 0;
    public static final int CMD_TYPE_HEART_BEAT = 1;
    public static final int CMD_TYPE_RELOGIN = 2;
    public static final int CMD_TYPE_REPORT_LOC = 3;
    
    public static final int CMD_TYPE_MY_SHARE_LIST = 4;
    public static final int CMD_TYPE_MY_BLACK_LIST = 5;
    public static final int CMD_TYPE_MY_ATTENTION_LIST = 6;
    
//    long returnCode;
    
    String jsonStr;
    
    long uid;
    
    private static final long REPORT_LOC_INTERVAL = 10 * 60 * 1000;
    private static final long HEART_BEAT_INTERVAL = 20 * 1000;
    
    private static final long RESEND_CMD_INTERVAL = 12 * 1000;
    
    
    private byte[] dataBuffer = null;
    long lastSendTime = 0;
    int sendCount = 0; // int32
    
    /**
     * 默认是解析不正常的
     */
    int parseError = PARSE_ERROR;
    public static final int PARSE_OK = 0;
    public static final int PARSE_ERROR = 1;
    
    public RECmd(Context context) {
        protocolVersion = ProtocolCommand.PROTOCOL_VERSION;
        sessionId = GeePrefrence.getMySessionId(context);
        uid = GeePrefrence.getMyUid(context);
    }
    
    /**
     * 在这里计算包长
     */
    private byte[] initSendJson(boolean isCmd) {
        if(!isCmd){
            return null;
        }
        byte[] body = null;
        if (null != jsonStr) {
            try {
                body = jsonStr.getBytes("UTF-8");
            } catch (UnsupportedEncodingException e) {
                LogUtil.e(TAG, "出现严重的问题，编码不支持UTF-8");
                return null;
            }
        } else {
            LogUtil.e(TAG, "jsonStr为null!!");
            return null;
        }
        
        // 包头16个字节
        packageLength = PACKAGE_HEAD_LENGTH + body.length;
        return body;
    }

    public int encode(ByteArrayBuffer buffer, boolean isCmd) {
        byte[] jsonByte = this.initSendJson(isCmd);
        encodeHead(buffer);
        if(isCmd){
            if (null != jsonByte) {
                buffer.append(jsonByte, 0, jsonByte.length);
            }else{
                return -1;
            }
        }
        return buffer.length();
    }
    
    private void encodeHead(ByteArrayBuffer buffer){
        LogUtil.v(TAG, "编码包头,packageLength: " + packageLength);
        byte[] b = ProtocolUtil.uShortToByteArray(packageLength);
        buffer.append(b, 0, 2);
        
        LogUtil.v(TAG, "编码包头,cmd: " + cmd);
        b = ProtocolUtil.uShortToByteArray(cmd);
        buffer.append(b, 0, 2);

        LogUtil.v(TAG, "编码包头,protocolVersion: " + protocolVersion);
        b = ProtocolUtil.uShortToByteArray(protocolVersion);
        buffer.append(b, 0, 2);

        LogUtil.v(TAG, "编码包头,sequenceID: " + sequenceID);
        b = ProtocolUtil.uShortToByteArray(sequenceID);
        buffer.append(b, 0, 2);

        LogUtil.v(TAG, "编码包头,sessionId: " + sessionId);
        b = ProtocolUtil.uIntToByteArray(sessionId);
        buffer.append(b, 0, 4);

        LogUtil.v(TAG, "编码包头,uid: " + uid);
        b = ProtocolUtil.uIntToByteArray(uid);
        buffer.append(b, 0, 4);
    }
    
    public void changeSession(long sid){
        if(null != dataBuffer && dataBuffer.length >= PACKAGE_HEAD_LENGTH){
            byte[] b = ProtocolUtil.uIntToByteArray(sessionId);
            dataBuffer[8] = b[0];
            dataBuffer[9] = b[1];
            dataBuffer[10] = b[2];
            dataBuffer[11] = b[3];
        }else{
            LogUtil.e(TAG, "更改session时发现有dataBuffer为null, cmd: " + cmd);
        }
    }
    
    /**
     * 调用一次说明发送了一次
     * @return
     */
    public byte[] getSendBuffer(Context context, ConcurrentHashMap<Integer, RECmd> reCmds) {
        switch (cmdType) {
        case CMD_TYPE_DEFAULT:
            if(needPerformSend(context, reCmds)){
                return dataBuffer;
            }
            break;
        case CMD_TYPE_HEART_BEAT:
            if(needHearBeat(context)){
                return dataBuffer;
            }
            break;
        case CMD_TYPE_RELOGIN:
            if(needRelogin()){
                return dataBuffer;
            }
            break;
        case CMD_TYPE_REPORT_LOC:
            if(needReportLoc(context)){
                return dataBuffer;
            }
            break;
        case CMD_TYPE_MY_SHARE_LIST:
            if(needSumitUntillSuccess()){
                return dataBuffer;
            }
            break;
        case CMD_TYPE_MY_BLACK_LIST:
            if(needSumitUntillSuccess()){
                return dataBuffer;
            }
            break;
        case CMD_TYPE_MY_ATTENTION_LIST:
            if(needSumitUntillSuccess()){
                return dataBuffer;
            }
            break;
        }
        return null;
    }
    
    public void initData() {
        ByteArrayBuffer byteArrayBuffer = new ByteArrayBuffer(PACKAGE_HEAD_LENGTH);
        int encodeLength = encode(byteArrayBuffer, true);
        if (packageLength == encodeLength) {
            dataBuffer = byteArrayBuffer.toByteArray();
            parseError = PARSE_OK;
        } else {
            LogUtil.e(TAG, "计算的包长和编码的包长度不一");
            parseError =  PARSE_ERROR;
        }
    }
    
    public void initMyUserData(Context context, int cmdType){
        isNeedNotifyUI = false;
        this.cmdType = cmdType;
        int serverType = -1;
        switch (cmdType) {
        case CMD_TYPE_MY_SHARE_LIST:
            sequenceID = RECmd.MY_SHARE_LIST_SEQUENCE_ID;
            serverType = 3;
            break;
        case CMD_TYPE_MY_BLACK_LIST:
            sequenceID = RECmd.MY_BLACK_LIST_SEQUENCE_ID;
            serverType = 2;
            break;
        case CMD_TYPE_MY_ATTENTION_LIST:
            sequenceID = RECmd.MY_ATTENTION_LIST_SEQUENCE_ID;
            serverType = 1;
            break;
        }
        cmd = ProtocolCommand.CMD_QUERY_MY_USER_LIST;
        JSONObject jo = new JSONObject();
        try {
            jo.put("type", serverType);
            jsonStr = jo.toString();
            initData();
        } catch (JSONException e) {
            LogUtil.e(TAG, "重新登录包初始化错误", e);
            parseError = PARSE_ERROR;
        }
    }
    
    public void initReloginData(Context context){
        isNeedNotifyUI = false;
        cmdType = CMD_TYPE_RELOGIN;
        sequenceID = RECmd.RELOGIN_SEQUENCE_ID;
        cmd = ProtocolCommand.CMD_LOGIN;
        sessionId = 0;
        JSONObject jo = new JSONObject();
        try {
            jo.put("type", 1);
            jo.put("account", GeePrefrence.getAccountName(context));
            jo.put("password", GeePrefrence.getPWD(context));
            jsonStr = jo.toString();
            initData();
        } catch (JSONException e) {
            LogUtil.e(TAG, "重新登录包初始化错误", e);
            parseError =  PARSE_ERROR;
        }
    }
    
    public void initHeartBeatData(){
        isNeedNotifyUI = false;
        cmdType = CMD_TYPE_HEART_BEAT;
        packageLength = PACKAGE_HEAD_LENGTH;
        sequenceID = HEART_BEAT_SEQUENCE_ID;
        cmd = ProtocolCommand.CMD_HEART_BEAT;
        ByteArrayBuffer byteArrayBuffer = new ByteArrayBuffer(PACKAGE_HEAD_LENGTH);
        int encodeLength = encode(byteArrayBuffer, false);
        if (PACKAGE_HEAD_LENGTH == encodeLength) {
            dataBuffer = byteArrayBuffer.toByteArray();
            parseError = PARSE_OK;
        } else {
            LogUtil.e(TAG, "心跳包初始化错误，计算的包长和编码的包长度不一");
            parseError =  PARSE_ERROR;
        }
    }
    
    public void initReportLocData(Context context){
        isNeedNotifyUI = false;
        cmdType = CMD_TYPE_REPORT_LOC;
        sequenceID = RECmd.REPORT_LOC_SEQUENCE_ID;
        cmd = ProtocolCommand.CMD_REPORT_LOCATION;
        String loc = GeePrefrence.getMyLocation(context);
        if(!CommonUtil.isEmpty(loc)){
            String[] lonAndLat = loc.split(",");
            if(null != lonAndLat && lonAndLat.length == 2){
                JSONObject jo = new JSONObject();
                try {
                    jo.put("longitude", lonAndLat[0]);
                    jo.put("latitude", lonAndLat[1]);
                    jsonStr = "{\"location\":" + jo.toString() + "}";
                    initData();
                    return;
                } catch (JSONException e) {
                    LogUtil.e(TAG, "上报位置信息包初始化错误", e);
                }
            }else{
                LogUtil.e(TAG, "读取已经存储的本地loc格式有异常");
            }
        }else{
            LogUtil.w(TAG, "读取已经存储的本地loc为null");
        }
        parseError =  PARSE_ERROR;
    }
    
    private boolean needPerformSend(Context context, ConcurrentHashMap<Integer, RECmd> reCmds) {
        //为了避免发送空包给服务端，造成服务端压力，需要进行数据验证...有时间补上其他字段
        if (packageLength == 0 || cmd == 0) {
            LogUtil.e(TAG, "长度0为空包，直接移除");
            reCmds.remove(sequenceID);
            return false;
        }
        //session过期了
        if (AndroidUtil.isSessionTimeOut(context)) {
            LogUtil.w(TAG, "sesion过期了，暂时不发送cmd包");
            return false;
        }
        //超过三次丢弃
        if (sendCount > 3) {
            LogUtil.w(TAG, "超过三次丢弃");
            reCmds.remove(sequenceID);
            return false;
        }
        //间隔超过10秒允许重发
        long now = System.currentTimeMillis();
        if ((now - lastSendTime) > RESEND_CMD_INTERVAL) {
            LogUtil.d(TAG, "sendCount:" + (sendCount+1));
            lastSendTime = now;
            sendCount++;
            return true;
        }
        return false;
    }
    
    /**
     * 直到重登成功为止, 超过4次还不成功，重连socket
     * @return
     */
    private boolean needRelogin() {
        if (sendCount > 4) {
            isServerException = true;
            LogUtil.e(TAG, "重登超过4次，判断服务器连接有异常，重连");
            return false;
        }
        long now = System.currentTimeMillis();
        if ((now - lastSendTime) > RESEND_CMD_INTERVAL) {
            lastSendTime = now;
            sendCount ++;
            LogUtil.w(TAG, "发送重新登录包");
            return true;
        }
        return false;
    }
    
    /**
     * 12秒一次提交，直到有成功返回为止
     * @return
     */
    private boolean needSumitUntillSuccess() {
        long now = System.currentTimeMillis();
        if ((now - lastSendTime) > RESEND_CMD_INTERVAL) {
            lastSendTime = now;
            sendCount ++;
            LogUtil.v(TAG, "发送查询黑名单、共享名单、关注名单包");
            return true;
        }
        return false;
    }
    
    /**
     * 心跳20秒提交一次
     * @param context
     * @return
     */
    private boolean needHearBeat(Context context){
        if (AndroidUtil.isSessionTimeOut(context)) {
            LogUtil.w(TAG, "sesion过期了，暂时不上报心跳");
            return false;
        }
        long now = System.currentTimeMillis();
        if ((now - lastSendTime) > HEART_BEAT_INTERVAL) {
            lastSendTime = now;
            LogUtil.v(TAG, "发送心跳包");
            return true;
        }
        return false;
    }
    
    /**
     * 10分钟上报一次loc
     * @param context
     * @return
     */
    private boolean needReportLoc(Context context){
        if (AndroidUtil.isSessionTimeOut(context)) {
            LogUtil.w(TAG, "sesion过期了，暂时不上报Loc");
            return false;
        }
        long now = System.currentTimeMillis();
        if ((now - lastSendTime) > REPORT_LOC_INTERVAL) {
            lastSendTime = now;
            LogUtil.v(TAG, "上报Loc包");
            return true;
        }
        return false;
    }
    
}
